package com.rednote.service.task;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rednote.entity.Posts;
import com.rednote.mapper.PostsMapper;
import com.rednote.service.impl.UserViewHistoryServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class SimilarityMatrixTask {

    @Autowired
    private PostsMapper postMapper;

    @Autowired
    private UserViewHistoryServiceImpl userViewHistoryService;

    @Autowired
    private StringRedisTemplate redisTemplate;
    private static final Logger logger = LoggerFactory.getLogger(ReactionSyncTask.class);

    // 每天凌晨3点执行
    //@Scheduled(cron = "0 0 3 * * ?")
    @Scheduled(cron = "0 0/1 * * * ?")// 每分钟执行一次用于测试
    public void updateSimilarityMatrix() {
        logger.info("定时任务相似度矩阵计算执行了 - {}");
        Map<Integer, List<Integer>> postUserMap = userViewHistoryService.getPostUserInteractions();
        List<Posts> allPosts = postMapper.selectList(null);

        // 映射：postId -> Posts对象
        Map<Integer, Posts> postMap = allPosts.stream()
                .collect(Collectors.toMap(Posts::getPostId, Function.identity()));

        // 只处理视频类型帖子
        List<Integer> videoPostIds = allPosts.stream()
                .filter(post -> post.getVideoUrl() != null && !post.getVideoUrl().isEmpty())
                .map(Posts::getPostId)
                .toList();

        videoPostIds.parallelStream().forEach(postId -> {
            Map<Integer, Double> similarityScores = new HashMap<>();

            for (Integer otherPostId : videoPostIds) {
                if (postId.equals(otherPostId)) continue;

                // 防止空指针
                List<Integer> usersA = postUserMap.getOrDefault(postId, new ArrayList<>());
                List<Integer> usersB = postUserMap.getOrDefault(otherPostId, new ArrayList<>());

                double cfScore = calculateCFSimilarity(usersA, usersB);
                double contentScore = calculateContentSimilarity(postMap.get(postId), postMap.get(otherPostId));

                double finalScore = 0.7 * cfScore + 0.3 * contentScore;
                similarityScores.put(otherPostId, finalScore);
            }

            // 取相似度Top100
            List<String> similarPosts = similarityScores.entrySet().stream()
                    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                    .limit(100)
                    .map(e -> String.valueOf(e.getKey()))
                    .collect(Collectors.toList());

            String key = "item_sim:" + postId;
            redisTemplate.delete(key);
            redisTemplate.opsForList().rightPushAll(key, similarPosts);
        });
    }

    public double calculateCFSimilarity(List<Integer> usersA, List<Integer> usersB) {
        if (usersA == null || usersB == null) return 0.0;

        Set<Integer> setA = new HashSet<>(usersA);
        Set<Integer> setB = new HashSet<>(usersB);

        Set<Integer> intersection = new HashSet<>(setA);
        intersection.retainAll(setB);

        double union = setA.size() + setB.size() - intersection.size();
        return union == 0 ? 0 : (double) intersection.size() / union;
    }

    public double calculateContentSimilarity(Posts postA, Posts postB) {
        double titleSimilarity = calculateTextSimilarity(postA.getTitle(), postB.getTitle());
        double tagSimilarity = calculateTagSimilarity(postA.getTag(), postB.getTag());
        return 0.6 * titleSimilarity + 0.4 * tagSimilarity;
    }

    public double calculateTagSimilarity(String tagA, String tagB) {
        if (tagA == null || tagB == null || tagA.isEmpty() || tagB.isEmpty()) return 0.0;

        Set<String> setA = new HashSet<>(Arrays.asList(tagA.split(",")));
        Set<String> setB = new HashSet<>(Arrays.asList(tagB.split(",")));

        Set<String> intersection = new HashSet<>(setA);
        intersection.retainAll(setB);

        Set<String> union = new HashSet<>(setA);
        union.addAll(setB);

        return union.isEmpty() ? 0.0 : (double) intersection.size() / union.size();
    }

    public double calculateTextSimilarity(String textA, String textB) {
        if (textA == null || textB == null || textA.isEmpty() || textB.isEmpty()) return 0.0;

        String[] wordsA = textA.toLowerCase().split("\\W+");
        String[] wordsB = textB.toLowerCase().split("\\W+");

        Map<String, Integer> freqA = new HashMap<>();
        for (String word : wordsA) {
            freqA.put(word, freqA.getOrDefault(word, 0) + 1);
        }

        Map<String, Integer> freqB = new HashMap<>();
        for (String word : wordsB) {
            freqB.put(word, freqB.getOrDefault(word, 0) + 1);
        }

        Set<String> allWords = new HashSet<>();
        allWords.addAll(freqA.keySet());
        allWords.addAll(freqB.keySet());

        int[] vecA = new int[allWords.size()];
        int[] vecB = new int[allWords.size()];

        int i = 0;
        for (String word : allWords) {
            vecA[i] = freqA.getOrDefault(word, 0);
            vecB[i] = freqB.getOrDefault(word, 0);
            i++;
        }

        int dotProduct = 0, normA = 0, normB = 0;
        for (i = 0; i < vecA.length; i++) {
            dotProduct += vecA[i] * vecB[i];
            normA += vecA[i] * vecA[i];
            normB += vecB[i] * vecB[i];
        }

        return (normA == 0 || normB == 0) ? 0.0 : dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
    }
}
